# -*- coding: utf-8 -*-
import os

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.Qt import *
from PyQt5.QtGui import *

from client.pages.ListItemWidget import *
from client.pages.NewFriendItemWidget import *
# from client.pages.BubbleWidget import Ui_BubbleWidget  # 因为重复引用会报错，所以把widget里的内容沾过来

from client.func.TimeCalc import get_time_text
from client.func.ClientHttp import response_friend_application_post, response_group_application_post, response_group_invitation_post

import webbrowser

root = "./"
# root = "../../"
img_dir = "client/.cache/img_buf/"

file_icon_size = (50, 50)  # 图片、文件略缩图尺寸(w, h)


class BackGround(QtWidgets.QGraphicsView):
    """窗口背景类 可以用于拖动无边框窗口"""
    _startPos = None
    _endPos = None
    _isTracking = False

    def __init(self, *args, **kwargs):
        super(BackGround, self).__init__(*args, **kwargs)
        self.initUI()

    def mouseMoveEvent(self, e: QtGui.QMouseEvent):  # 重写移动事件
        self._endPos = e.pos() - self._startPos
        self.parent().parent().move(self.parent().parent().pos() + self._endPos)

    def mousePressEvent(self, e: QtGui.QMouseEvent):
        if e.button() == Qt.LeftButton:
            self._isTracking = True
            self._startPos = QPoint(e.x(), e.y())

    def mouseReleaseEvent(self, e: QtGui.QMouseEvent):
        if e.button() == Qt.LeftButton:
            self._isTracking = False
            self._startPos = None
            self._endPos = None


class ListItemWidget(QWidget, Ui_ListItemWidget):
    """好友列表窗口"""

    def __init__(self):
        super(ListItemWidget, self).__init__()
        self.setupUi(self)

    def enterEvent(self, a0):
        self.background.setStyleSheet("QGraphicsView{\n"
                                      "background-color: rgb(255, 255, 255);\n"
                                      "border-bottom:2px solid rgb(153, 131, 110);\n"
                                      "border-top:none;\n"
                                      "border-left:none;\n"
                                      "border-right:none;\n"
                                      "}\n"
                                      )

    def leaveEvent(self, a0):
        self.background.setStyleSheet("QGraphicsView{\n"
                                      "background-color: rgb(242, 232, 220);\n"
                                      "border-bottom:2px solid rgb(153, 131, 110);\n"
                                      "border-top:none;\n"
                                      "border-left:none;\n"
                                      "border-right:none;\n"
                                      "}\n"
                                      )


class ListItem(QtWidgets.QListWidgetItem):
    """列表项类 用于实现好友列表"""

    def __init__(self, *args, **kwargs):
        super(ListItem, self).__init__(*args, **kwargs)
        self.widget = ListItemWidget()
        self.setSizeHint(self.widget.size())
        self.uid = "000001"
        self.name = self.uid
        self.category = "friend"  # "friend", "group"
        self.img_path = root + "client/.cache/img_buf/user_default.png"
        self.msg_num = 0
        self.new_msg = ""
        self.max_name = 9  # 名字最长可见9个汉字
        self.max_msg = 13  # 最新消息最长可见13个汉字

        self.set_img(self.img_path)
        self.update()

    def update(self):
        self.msg_num_update()
        self.new_msg_update()
        self.name_update()

    def set_img(self, path):
        if os.path.exists(path):
            img = QtGui.QPixmap(path).scaled(self.widget.user_img.width(), self.widget.user_img.height())
            self.widget.user_img.setPixmap(img)

    def msg_num_update(self):
        if self.msg_num > 0:
            if self.msg_num > 99:
                self.widget.msg_label.setText("99+")
            else:
                self.widget.msg_label.setText(str(self.msg_num))
            self.widget.msg_label.show()
        else:
            self.widget.msg_label.hide()

    def new_msg_update(self):
        if len(self.new_msg) > self.max_msg:
            self.widget.new_msg_label.setText(self.new_msg[:14] + "...")
        else:
            self.widget.new_msg_label.setText(self.new_msg)

    def name_update(self):
        if len(self.name) > self.max_name:
            self.widget.name_label.setText(self.name[:self.max_name+1] + "...")
        else:
            self.widget.name_label.setText(self.name)


class NewFriendItemWidget(QWidget, Ui_NewFriendItemWidget):
    """新朋友列表窗口"""

    def __init__(self):
        super(NewFriendItemWidget, self).__init__()
        self.setupUi(self)

    def enterEvent(self, a0):
        self.background.setStyleSheet("QGraphicsView{\n"
                                      "background-color: rgb(255, 255, 255);\n"
                                      "border-bottom:2px solid rgb(153, 131, 110);\n"
                                      "border-top:none;\n"
                                      "border-left:none;\n"
                                      "border-right:none;\n"
                                      "}\n"
                                      )

    def leaveEvent(self, a0):
        self.background.setStyleSheet("QGraphicsView{\n"
                                      "background-color: rgb(242, 232, 220);\n"
                                      "border-bottom:2px solid rgb(153, 131, 110);\n"
                                      "border-top:none;\n"
                                      "border-left:none;\n"
                                      "border-right:none;\n"
                                      "}\n"
                                      )


class NewFriendItem(QtWidgets.QListWidgetItem):
    """新朋友消息项类"""

    def __init__(self, *args, **kwargs):
        super(NewFriendItem, self).__init__(*args, **kwargs)
        self.widget = NewFriendItemWidget()
        self.setSizeHint(self.widget.size())
        self.myuid = ""
        self.uid = ""
        self.group_uid = ""
        self.name = self.uid
        self.info = ""
        self.category = "friend"  # "friend", "group_apply", "group_invite"
        self.isSolve = False
        self.img_path = root + "client/.cache/img_buf/user_default.png"
        self.max_name = 9  # 名字最长可见9个汉字

        self.widget.agree_button.clicked.connect(self.OnAgree)
        self.widget.refuse_button.clicked.connect(self.OnRefuse)

    def OnAgree(self):
        if self.category == "friend":
            reply = response_friend_application_post(self.myuid, self.uid, True)
        elif self.category == "group_apply":
            reply = response_group_application_post(self.myuid, self.uid, self.group_uid, True)
        else:
            reply = response_group_invitation_post(self.myuid, self.group_uid, True)
        self.isSolve = reply
        self.info = u"消息已处理"
        self.update()

    def OnRefuse(self):
        if self.category == "friend":
            reply = response_friend_application_post(self.myuid, self.uid, False)
        elif self.category == "group_apply":
            reply = response_group_application_post(self.myuid, self.uid, self.group_uid, False)
        else:
            reply = response_group_invitation_post(self.myuid, self.group_uid, False)
        self.isSolve = reply
        self.info = u"消息已处理"
        self.update()

    def calc_relationship(self):
        if self.isSolve:
            self.widget.agree_button.hide()
            self.widget.refuse_button.hide()
        else:
            self.widget.agree_button.show()
            self.widget.refuse_button.show()

    def update(self):
        self.set_img(self.img_path)
        self.name_update()
        self.calc_relationship()
        self.widget.label.setText(self.info)

    def set_img(self, path):
        if os.path.exists(path):
            img = QtGui.QPixmap(path).scaled(self.widget.user_img.width(), self.widget.user_img.height())
            self.widget.user_img.setPixmap(img)

    def name_update(self):
        if len(self.name) > self.max_name:
            self.widget.name_label.setText(self.name[:10] + "...")
        else:
            self.widget.name_label.setText(self.name)


class RichTextEdit(QTextBrowser):
    """富文本框，实现拖拽与粘贴图片、文件等; 以及点击图片放大和点击文件下载"""

    def __init__(self, *args, **kwargs):
        super(RichTextEdit, self).__init__(*args, **kwargs)
        self.setAttribute(Qt.WA_InputMethodEnabled, True)  # 不加这个不能输入中文
        self.anchorClicked.connect(self.click_img)

    def click_img(self, a0):
        url = a0.toString()
        # print(url)
        url_path, url_type = url.split("?")
        if "img" in url_type:
            webbrowser.open(url_path)
        else:
            webbrowser.open(url_path)

    def canInsertFromMimeData(self, source: QtCore.QMimeData) -> bool:
        """重写拖放函数"""
        return super(RichTextEdit, self).canInsertFromMimeData(source)

    def insertFromMimeData(self, source: QtCore.QMimeData) -> None:
        """重写粘贴函数"""
        if source.hasUrls():
            for url in source.urls():
                file_info = QFileInfo(url.toLocalFile())
                if os.path.getsize(url.toLocalFile())//1024 < 1024:  # 图片大于1M当作文件处理
                    ext = file_info.suffix().lower()
                    if ext in QImageReader.supportedImageFormats():
                        self.insert_image(QImage(file_info.filePath()), url, ext)
                    else:
                        self.insert_file(url)
                else:
                    self.insert_file(url)
        else:
            super(RichTextEdit, self).insertFromMimeData(source)

    def insert_image(self, image: QImage, url, fmt: str = "png"):
        """插入图片"""
        global file_icon_size
        # print(url.toString())
        data = QByteArray()
        buffer = QBuffer(data)
        image.save(buffer, fmt)
        base64_data = str(data.toBase64())[2:-1]
        file_path = url.toString()
        file_name = os.path.basename(file_path)
        data = '<a href={}><img src="data:{};base64,{}" width="{}" height="{}" title="img:{}"/></a> '.format(
            file_path + "?img",
            url.toString(),
            base64_data,
            file_icon_size[0], file_icon_size[1],
            file_name
        )
        # print(data)
        fragment = QTextDocumentFragment.fromHtml(data)
        self.textCursor().insertFragment(fragment)
        self.textCursor().insertText("")  # 不加这句之后添加文本会带超链接

    def insert_file(self, url: QUrl):
        """插入文件"""
        file_path = url.toString()
        file_name = os.path.basename(file_path)
        html_data = '<a href={}><img src="{}" width="{}" height="{}" title="file:{}"/></a> '.format(
            file_path + "?file",
            root + "client/src/file_type.png",
            file_icon_size[0], file_icon_size[1],
            file_name
        )
        fragment = QTextDocumentFragment.fromHtml(html_data)
        self.textCursor().insertFragment(fragment)
        self.textCursor().insertText("")  # 不加这句之后添加文本会带超链接


class BubbleWidget(QWidget):
    """聊天气泡窗口, 参考了https://blog.csdn.net/weixin_41673576/article/details/123462473"""
    update_signal = pyqtSignal()

    def __init__(self):
        super(BubbleWidget, self).__init__()
        self.setupUi(self)
        self.name = u"匿名用户"  # 发送者姓名
        self.img_path = root + "client/.cache/img_buf/user_default.png"  # 头像图片路径
        self.text = ""  # 文本内容
        self.bubble_type = "text"  # 消息类型  "text" 或 "img" 或 "file"
        self.status = "normal"  # 消息状态: normal, waiting, error, finish
        self.isMe = False  # 是不是我的消息 用于判断放在左边或者右边
        self.direction = "left"  # 消息放在哪边: left, right
        self.time = "1672502400"  # "2023-01-01 00:00:00"19  # 消息时间戳
        self.time_text = "2023-01-01 00:00:00"  # 时间文本
        self.show_time = True  # 是否显示时间, 由两条消息之间的间隔决定
        self.img_size = [20, 16, 40, 40]  # 头像初始坐标及尺寸[x,y,w,h]
        self.bubble_size = [0, 0, 0, 0]  # 气泡坐标及尺寸[x,y,w,h]
        self.text_size = [0, 0, 0, 0]  # 文字坐标及尺寸[x,y,w,h]
        self.name_size = [0, 16, 0, 0]  # 名字坐标及尺寸[x,y,w,h]
        self.time_size = [0, 16, 0, 0]  # 时间坐标及尺寸[x,y,w,h]
        self.status_size = [0, 0, 0, 0]  # 状态坐标及尺寸[x,y,w,h]
        self.bubble_color = (255, 255, 255)  # 气泡颜色
        self.mybubble_color = (153, 131, 110)  # 我的气泡颜色
        self.text_color = (153, 131, 110)  # 文字颜色
        self.mytext_color = (255, 255, 255)  # 我的文字颜色
        self.other_color = QColor(153, 131, 110)  # 其他文字颜色
        self.text_font = QtGui.QFont("Comic Sans MS", 10)  # 消息字体
        self.name_font = QtGui.QFont("Comic Sans MS", 10)  # 姓名字体
        self.other_font = QtGui.QFont("Comic Sans MS", 8)  # 其他内容字体

    def calc_size(self):
        global file_icon_size
        begin_width_spacing, begin_height_spacing, icon_width, icon_height = 20, 16, 40, 40
        text_width_spacing, text_height_spacing = 12, 12
        status_width, status_height = 15, 15
        space_between = 6
        # 计算头像位置
        flip_width = 1400  # 消息框超过这个宽度，我的消息将从右边变到左边
        if self.isMe and self.width() < flip_width:
            self.direction = "right"
            self.img_size[0] = self.width() - begin_width_spacing - icon_width
        else:
            self.direction = "left"
            self.img_size[0] = begin_width_spacing
        self.img_size[1:4] = begin_height_spacing, icon_width, icon_height
        # 计算文字气泡
        if self.bubble_type == "text":
            metrics = QFontMetrics(self.text_font)  # 字符尺寸计算器
            if metrics.width("A") * 2 + begin_height_spacing * 1.5 > text_width_spacing:
                text_min_width = begin_height_spacing * 1.5 - text_width_spacing
            else:
                text_min_width = 0
            min_width = (begin_width_spacing + icon_width + text_width_spacing) * 2 + space_between
            if self.width() < min_width + text_min_width:
                self.setMinimumWidth(min_width + text_min_width)
            text_max_width = self.width() - min_width
            real_width = metrics.width(self.text)
            if real_width < text_max_width:
                text_max_width = real_width
        else:
            text_max_width = file_icon_size[0]
        text_height = self.textEdit.document().size().height()
        self.bubble_size[1] = begin_height_spacing + icon_height // 2
        self.bubble_size[2] = text_width_spacing * 2 + text_max_width
        self.bubble_size[3] = text_height_spacing * 2 + text_height
        if self.direction == "left":
            self.bubble_size[0] = begin_width_spacing + icon_width + space_between
        else:
            self.bubble_size[0] = self.width() - begin_width_spacing - icon_width - self.bubble_size[2] - space_between
        self.resize(QSize(self.width(), self.bubble_size[3] + text_height_spacing * 2 + icon_height // 2))
        self.update_signal.emit()
        # 计算文字
        self.text_size[0] = self.bubble_size[0] + text_width_spacing - 2
        self.text_size[1] = self.bubble_size[1] + text_height_spacing - 2
        self.text_size[2] = text_max_width + 8
        self.text_size[3] = text_height + 12
        # 计算状态标识
        if self.direction == "left":
            self.status_size[0] = self.bubble_size[0] + self.bubble_size[2]
        else:
            self.status_size[0] = self.bubble_size[0] - status_width
        self.status_size[1] = self.bubble_size[1] + self.bubble_size[3] - status_height
        self.status_size[2:4] = status_width, status_height
        # 计算名字
        metrics = QFontMetrics(self.name_font)
        self.name_size[1] = begin_height_spacing // 2
        self.name_size[2] = metrics.width(self.name)
        self.name_size[3] = metrics.height()
        if self.direction == "left":
            self.name_size[0] = self.bubble_size[0]
        else:
            self.name_size[0] = self.bubble_size[0] + self.bubble_size[2] - self.name_size[2]
        # 计算时间
        self.time_text = get_time_text(self.time)
        metrics = QFontMetrics(self.other_font)
        self.time_size[1] = 0
        self.time_size[2] = metrics.width(self.time_text)
        self.time_size[3] = metrics.height()
        self.time_size[0] = self.width() // 2 - self.time_size[2] // 2

    def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
        """重写绘图事件"""
        paint = QPainter()
        paint.begin(self)
        paint.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform)

        self.calc_size()
        self.draw_head_img(paint)
        self.draw_name(paint)
        self.draw_time(paint)
        self.draw_bubble(paint)
        self.draw_status(paint)
        self.textEdit.setGeometry(*self.text_size)

        paint.end()

    def set_text(self):
        self.textEdit.setFont(self.text_font)
        if self.bubble_type == "text":
            self.textEdit.setText(self.text)
        else:
            self.textEdit.insertHtml(self.text)
        if self.isMe:
            self.textEdit.setStyleSheet(
                "background-color: rgb({}, {}, {});\n".format(*self.mybubble_color) +
                "color: rgb({}, {}, {});\n".format(*self.mytext_color) +
                "border:none;\n"
            )
        else:
            self.textEdit.setStyleSheet(
                "background-color: rgb({}, {}, {});\n".format(*self.bubble_color) +
                "color: rgb({}, {}, {});\n".format(*self.text_color) +
                "border:none;\n"
            )

    def draw_head_img(self, paint):
        icon_rect = QRect(*self.img_size)
        paint.setPen(Qt.NoPen)
        paint.setBrush(QBrush(Qt.gray))
        paint.drawPixmap(icon_rect, QtGui.QPixmap(self.img_path))

    def draw_bubble(self, paint):
        bubbleRect = QRect(*self.bubble_size)
        paint.setPen(Qt.NoPen)
        if self.isMe:
            paint.setBrush(QBrush(QColor(*self.mybubble_color)))
        else:
            paint.setBrush(QBrush(QColor(*self.bubble_color)))
        paint.drawRoundedRect(
            bubbleRect.x(),
            bubbleRect.y(),
            bubbleRect.width(),
            bubbleRect.height(),
            14, 14
        )
        if self.direction == "left":
            paint.drawRect(bubbleRect.x(), bubbleRect.y(), 14, 14)
        else:
            paint.drawRect(bubbleRect.x() + bubbleRect.width() - 14, bubbleRect.y(), 14, 14)

    def draw_status(self, paint):
        icon_rect = QRect(*self.status_size)
        paint.setPen(Qt.NoPen)
        paint.setBrush(QBrush(Qt.gray))
        if self.status == "error":
            paint.drawPixmap(icon_rect, QtGui.QPixmap(root + "client/src/error.png"))
        elif self.status == "waiting":
            paint.drawPixmap(icon_rect, QtGui.QPixmap(root + "client/src/loading.png"))
        elif self.status == "finish":
            paint.drawPixmap(icon_rect, QtGui.QPixmap(root + "client/src/ok.png"))

    def draw_time(self, paint):
        if self.show_time:
            paint.setFont(self.other_font)
            paint.setPen(self.other_color)
            paint.drawText(
                QRect(*self.time_size),
                Qt.AlignCenter, self.time_text
            )

    def draw_name(self, paint):
        paint.setFont(self.name_font)
        paint.setPen(self.other_color)
        paint.drawText(
            QRect(*self.name_size),
            Qt.AlignCenter, self.name
        )

    def set_width(self, width):
        self.resize(QSize(width, self.height()))
        self.update_signal.emit()
        # print(self.size())

    def setupUi(self, BubbleWidget):
        BubbleWidget.setObjectName("BubbleWidget")
        BubbleWidget.resize(665, 79)
        BubbleWidget.setFocusPolicy(Qt.StrongFocus)
        self.textEdit = RichTextEdit(BubbleWidget)
        self.textEdit.setGeometry(QtCore.QRect(0, 0, 0, 0))
        self.textEdit.setStyleSheet("border:none;")
        self.textEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.textEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.textEdit.setReadOnly(True)
        self.textEdit.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.TextSelectableByMouse)
        self.textEdit.setPlaceholderText("")
        self.textEdit.setOpenLinks(False)
        self.textEdit.setObjectName("textEdit")

        self.retranslateUi(BubbleWidget)
        QtCore.QMetaObject.connectSlotsByName(BubbleWidget)

    def retranslateUi(self, BubbleWidget):
        _translate = QtCore.QCoreApplication.translate
        BubbleWidget.setWindowTitle(_translate("BubbleWidget", "bubble"))
        self.textEdit.setHtml(_translate("BubbleWidget",
                                         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
                                         "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
                                         "p, li { white-space: pre-wrap; }\n"
                                         "</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
                                         "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))


class Bubble(QtWidgets.QListWidgetItem):
    """聊天气泡"""

    def __init__(self, *args, **kwargs):
        super(Bubble, self).__init__(*args, **kwargs)
        self.widget = BubbleWidget()
        self.widget.update_signal.connect(self.update)

    def update(self):
        self.setSizeHint(self.widget.size())

    def init_data(self, dic):
        """dict 包含 'name', 'text', 'img_path', 'time', 'isMe', 'bubble_type'"""
        self.widget.name = dic["name"]
        self.widget.text = dic["text"]
        self.widget.time = dic["time"]
        self.widget.isMe = dic["isMe"]
        self.widget.bubble_type = dic["bubble_type"]
        if os.path.exists(root + img_dir + dic["img_path"]):
            self.widget.img_path = root + img_dir + dic["img_path"]
        self.widget.set_text()
